/**
 * \file IfxGtm_Pwm.h
 * \brief GTM PWM details
 * \ingroup IfxLld_Gtm
 *
 * \version iLLD_1_0_1_17_0
 * \copyright Copyright (c) 2023 Infineon Technologies AG. All rights reserved.
 *
 *
 *
 *                                 IMPORTANT NOTICE
 *
 * Use of this file is subject to the terms of use agreed between (i) you or
 * the company in which ordinary course of business you are acting and (ii)
 * Infineon Technologies AG or its licensees. If and as long as no such terms
 * of use are agreed, use of this file is subject to following:
 *
 * Boost Software License - Version 1.0 - August 17th, 2003
 *
 * Permission is hereby granted, free of charge, to any person or organization
 * obtaining a copy of the software and accompanying documentation covered by
 * this license (the "Software") to use, reproduce, display, distribute,
 * execute, and transmit the Software, and to prepare derivative works of the
 * Software, and to permit third-parties to whom the Software is furnished to
 * do so, all subject to the following:
 *
 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer, must
 * be included in all copies of the Software, in whole or in part, and all
 * derivative works of the Software, unless such copies or derivative works are
 * solely in the form of machine-executable object code generated by a source
 * language processor.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 *
 *
 * \defgroup IfxLld_Gtm_Pwm PWM
 * \ingroup IfxLld_Gtm
 */

#ifndef IFXGTM_PWM_H
#define IFXGTM_PWM_H 1

/******************************************************************************/
/*----------------------------------Includes----------------------------------*/
/******************************************************************************/

#include "Gtm/Std/IfxGtm_Cmu.h"
#include "Gtm/Std/IfxGtm_Dtm.h"
#include "Gtm/Std/IfxGtm_Tom.h"
#include "Gtm/Std/IfxGtm.h"
#include "Gtm/Trig/IfxGtm_Trig.h"
#include "_PinMap/IfxGtm_PinMap.h"
#include "Gtm/Std/IfxGtm_Atom.h"

/******************************************************************************/
/*-----------------------------------Macros-----------------------------------*/
/******************************************************************************/

/** \brief Get TOM global control unit index with help of channel index
 */
#define IFXGTM_PWM_GET_TGC_INDEX(channelIndex) ((uint8)(((uint8)(channelIndex)) >> 3))

/** \brief Default PWM frequency in Hz
 */
#ifndef IFXGTM_PWM_DEFAULT_FREQUENCY
#define IFXGTM_PWM_DEFAULT_FREQUENCY ((float32)(10000.0f))
#endif

/** \brief Check if submodule is present in cluster
 */
#define IFXGTM_PWM_IS_SUBMODULE_PRESENT(cluster, subModule) \
    ((uint32)((subModule == IfxGtm_Pwm_SubModule_atom) ||   \
              ((subModule == IfxGtm_Pwm_SubModule_tom) &&   \
               ((uint8)cluster < IFXGTM_NUM_TOM_OBJECTS))))

/** \brief Check if channel is present in submodule
 */
#define IFXGTM_PWM_IS_CHANNEL_PRESENT(subModule, channel)  \
    ((uint32)((subModule == IfxGtm_Pwm_SubModule_tom) ||   \
              ((subModule == IfxGtm_Pwm_SubModule_atom) && \
               ((uint8)channel < IFXGTM_NUM_ATOM_CHANNELS))))

/** \brief Mask for range of channel bitfields in Global control
 */
#define IFXGTM_PWM_CHANNELS_MASK(firstChannel, lastChannel) ((uint32)(((uint32)1u << ((uint32)((uint32)lastChannel - (uint32)firstChannel + 1u) << 1u)) - 1u) << (((uint32)firstChannel & 0x7u) << 1u))

/** \brief Value to enable UPEN of all channels in global control register
 */
#define IFXGTM_PWM_UPEN_ENABLE_VALUE                                                      \
    (((uint32)IfxGtm_FeatureControl_enable << IFX_GTM_ATOM_AGC_GLB_CTRL_UPEN_CTRL0_OFF) | \
     ((uint32)IfxGtm_FeatureControl_enable << IFX_GTM_ATOM_AGC_GLB_CTRL_UPEN_CTRL1_OFF) | \
     ((uint32)IfxGtm_FeatureControl_enable << IFX_GTM_ATOM_AGC_GLB_CTRL_UPEN_CTRL2_OFF) | \
     ((uint32)IfxGtm_FeatureControl_enable << IFX_GTM_ATOM_AGC_GLB_CTRL_UPEN_CTRL3_OFF) | \
     ((uint32)IfxGtm_FeatureControl_enable << IFX_GTM_ATOM_AGC_GLB_CTRL_UPEN_CTRL4_OFF) | \
     ((uint32)IfxGtm_FeatureControl_enable << IFX_GTM_ATOM_AGC_GLB_CTRL_UPEN_CTRL5_OFF) | \
     ((uint32)IfxGtm_FeatureControl_enable << IFX_GTM_ATOM_AGC_GLB_CTRL_UPEN_CTRL6_OFF) | \
     ((uint32)IfxGtm_FeatureControl_enable << IFX_GTM_ATOM_AGC_GLB_CTRL_UPEN_CTRL7_OFF))

/** \brief Value to disable UPEN of all channels in global control register
 */
#define IFXGTM_PWM_UPEN_DISABLE_VALUE                                                      \
    (((uint32)IfxGtm_FeatureControl_disable << IFX_GTM_ATOM_AGC_GLB_CTRL_UPEN_CTRL0_OFF) | \
     ((uint32)IfxGtm_FeatureControl_disable << IFX_GTM_ATOM_AGC_GLB_CTRL_UPEN_CTRL1_OFF) | \
     ((uint32)IfxGtm_FeatureControl_disable << IFX_GTM_ATOM_AGC_GLB_CTRL_UPEN_CTRL2_OFF) | \
     ((uint32)IfxGtm_FeatureControl_disable << IFX_GTM_ATOM_AGC_GLB_CTRL_UPEN_CTRL3_OFF) | \
     ((uint32)IfxGtm_FeatureControl_disable << IFX_GTM_ATOM_AGC_GLB_CTRL_UPEN_CTRL4_OFF) | \
     ((uint32)IfxGtm_FeatureControl_disable << IFX_GTM_ATOM_AGC_GLB_CTRL_UPEN_CTRL5_OFF) | \
     ((uint32)IfxGtm_FeatureControl_disable << IFX_GTM_ATOM_AGC_GLB_CTRL_UPEN_CTRL6_OFF) | \
     ((uint32)IfxGtm_FeatureControl_disable << IFX_GTM_ATOM_AGC_GLB_CTRL_UPEN_CTRL7_OFF))

/** \brief Number of TBU ticks after at which Atom/Tom channel received trigger to start counters
 */
#define IFXGTM_PWM_TBU_TIMEOUT_TICKS ((uint32)0x50u)

/******************************************************************************/
/*------------------------------Type Definitions------------------------------*/
/******************************************************************************/

/** \brief Interrupt callback function type definition
 */
typedef void (*IfxGtm_Pwm_callBack)(void *data);

/******************************************************************************/
/*-------------------------------Enumerations---------------------------------*/
/******************************************************************************/

/** \brief PWM Alignment
 */
typedef enum
{
    IfxGtm_Pwm_Alignment_edge   = 0, /**< \brief Edge aligned PWM */
    IfxGtm_Pwm_Alignment_center = 1  /**< \brief Symmetric center aligned PWM */
} IfxGtm_Pwm_Alignment;

/** \brief PWM Channel state
 */
typedef enum
{
    IfxGtm_Pwm_ChannelState_running = 0,  /**< \brief Channel counters are running and output is enabled */
    IfxGtm_Pwm_ChannelState_stopped       /**< \brief Either channel or it's output is disabled */
} IfxGtm_Pwm_ChannelState;

/** \brief Reset event for channel counter CN0
 */
typedef enum
{
    IfxGtm_Pwm_ResetEvent_onCm0     = 0, /**< \brief Reset counter when CN0 = CM0 */
    IfxGtm_Pwm_ResetEvent_onTrigger = 1  /**< \brief Reset counter when trigger is received */
} IfxGtm_Pwm_ResetEvent;

/** \brief Module state
 */
typedef enum
{
    IfxGtm_Pwm_State_unknown = -1,  /**< \brief Unknown state */
    IfxGtm_Pwm_State_init    = 0,   /**< \brief Module is being initialized */
    IfxGtm_Pwm_State_run     = 1,   /**< \brief Run state */
    IfxGtm_Pwm_State_stopped = 2,   /**< \brief Stopped state */
    IfxGtm_Pwm_State_error   = 3    /**< \brief Error state */
} IfxGtm_Pwm_State;

/** \brief GTM Submodules
 */
typedef enum
{
    IfxGtm_Pwm_SubModule_atom = 0,  /**< \brief GTM submodule ATOM */
    IfxGtm_Pwm_SubModule_tom  = 1   /**< \brief GTM submodule TOM */
} IfxGtm_Pwm_SubModule;

/** \brief PWM Channels
 */
typedef enum
{
    IfxGtm_Pwm_SubModule_Ch_0  = 0,   /**< \brief ATOM / TOM channel 0 */
    IfxGtm_Pwm_SubModule_Ch_1  = 1,   /**< \brief ATOM / TOM channel 1 */
    IfxGtm_Pwm_SubModule_Ch_2  = 2,   /**< \brief ATOM / TOM channel 2 */
    IfxGtm_Pwm_SubModule_Ch_3  = 3,   /**< \brief ATOM / TOM channel 3 */
    IfxGtm_Pwm_SubModule_Ch_4  = 4,   /**< \brief ATOM / TOM channel 4 */
    IfxGtm_Pwm_SubModule_Ch_5  = 5,   /**< \brief ATOM / TOM channel 5 */
    IfxGtm_Pwm_SubModule_Ch_6  = 6,   /**< \brief ATOM / TOM channel 6 */
    IfxGtm_Pwm_SubModule_Ch_7  = 7,   /**< \brief ATOM / TOM channel 7 */
    IfxGtm_Pwm_SubModule_Ch_8  = 8,   /**< \brief TOM channel 8 */
    IfxGtm_Pwm_SubModule_Ch_9  = 9,   /**< \brief TOM channel 9 */
    IfxGtm_Pwm_SubModule_Ch_10 = 10,  /**< \brief TOM channel 10 */
    IfxGtm_Pwm_SubModule_Ch_11 = 11,  /**< \brief TOM channel 11 */
    IfxGtm_Pwm_SubModule_Ch_12 = 12,  /**< \brief TOM channel 12 */
    IfxGtm_Pwm_SubModule_Ch_13 = 13,  /**< \brief TOM channel 13 */
    IfxGtm_Pwm_SubModule_Ch_14 = 14,  /**< \brief TOM channel 14 */
    IfxGtm_Pwm_SubModule_Ch_15 = 15   /**< \brief TOM channel 15 */
} IfxGtm_Pwm_SubModule_Ch;

/** \brief Index to access individual channel information from array of channels
 */
typedef enum
{
    IfxGtm_Pwm_SyncChannelIndex_0 = 0,  /**< \brief Base Channel */
    IfxGtm_Pwm_SyncChannelIndex_1,      /**< \brief Sync Channel 0 */
    IfxGtm_Pwm_SyncChannelIndex_2,      /**< \brief Sync Channel 1 */
    IfxGtm_Pwm_SyncChannelIndex_3,      /**< \brief Sync Channel 2 */
    IfxGtm_Pwm_SyncChannelIndex_4,      /**< \brief Sync Channel 3 */
    IfxGtm_Pwm_SyncChannelIndex_5,      /**< \brief Sync Channel 4 */
    IfxGtm_Pwm_SyncChannelIndex_6,      /**< \brief Sync Channel 5 */
    IfxGtm_Pwm_SyncChannelIndex_7,      /**< \brief Sync Channel 6 */
    IfxGtm_Pwm_SyncChannelIndex_8,      /**< \brief Sync Channel 7 */
    IfxGtm_Pwm_SyncChannelIndex_9,      /**< \brief Sync Channel 8 */
    IfxGtm_Pwm_SyncChannelIndex_10,     /**< \brief Sync Channel 9 */
    IfxGtm_Pwm_SyncChannelIndex_11,     /**< \brief Sync Channel 10 */
    IfxGtm_Pwm_SyncChannelIndex_12,     /**< \brief Sync Channel 11 */
    IfxGtm_Pwm_SyncChannelIndex_13,     /**< \brief Sync Channel 12 */
    IfxGtm_Pwm_SyncChannelIndex_14,     /**< \brief Sync Channel 13 */
    IfxGtm_Pwm_SyncChannelIndex_15      /**< \brief Sync Channel 14 */
} IfxGtm_Pwm_SyncChannelIndex;

/******************************************************************************/
/*-----------------------------Data Structures--------------------------------*/
/******************************************************************************/

/** \brief Dead time structure
 */
typedef struct
{
    float32 rising;        /**< \brief Relative rise time */
    float32 falling;       /**< \brief Relative fall time */
} IfxGtm_Pwm_DeadTime;

/** \brief Union of Atom and Tom Tout map
 */
typedef union
{
    IfxGtm_Atom_ToutMap atom;       /**< \brief ATOM map */
    IfxGtm_Tom_ToutMap  tom;        /**< \brief TOM map */
} IfxGtm_Pwm_ToutMap;

/** \brief Structure to store address of frequently used registers
 */
typedef struct
{
    volatile uint32 *SR0;              /**< \brief Pointer to ATOM/TOM SR0 */
    volatile uint32 *SR1;              /**< \brief Pointer to ATOM/TOM SR1 */
    volatile uint32 *CM0;              /**< \brief Pointer to ATOM/TOM CM0 */
    volatile uint32 *CM1;              /**< \brief Pointer to ATOM/TOM CM1 */
    volatile uint32 *CN0;              /**< \brief Pointer to ATOM/TOM CN0 */
    volatile uint32 *CTRL;             /**< \brief Pointer to ATOM/TOM CTRL */
    volatile uint32 *GLB_CTRL;         /**< \brief Pointer to ATOM/TOM GLB_CTRL for disabling/enabling UPEN */
    volatile uint32 *IRQ_NOTIFY;       /**< \brief Pointer to ATOM/TOM IRQ_NOTIFY for reading/clearing interrupts */
    volatile uint32 *DTV;              /**< \brief Pointer to DTM dead time value register */
} IfxGtm_Pwm_ChannelRegisters;

typedef struct
{
    IfxGtm_Pwm_DeadTime deadTime;       /**< \brief Dead time in seconds */
} IfxGtm_Pwm_DtmConfig;

/** \brief Interrupt Configuration structure
 */
typedef struct
{
    IfxGtm_IrqMode      mode;              /**< \brief IRQ mode of interrupt. Note: Use IfxGtm_IrqMode_pulseNotify as default */
    IfxSrc_Tos          isrProvider;       /**< \brief Type of Service for Ccu0/1 interrupt */
    Ifx_Priority        priority;          /**< \brief Priority for Ccu0/1 interrupt */
    IfxGtm_Pwm_callBack periodEvent;       /**< \brief Period interrupt callback function pointer */
    IfxGtm_Pwm_callBack dutyEvent;         /**< \brief Duty interrupt callback function pointer */
} IfxGtm_Pwm_InterruptConfig;

/** \brief PWM Pin output configuration structure
 */
typedef struct
{
    IfxGtm_Pwm_ToutMap *pin;                        /**< \brief Output pin configuration */
    IfxGtm_Pwm_ToutMap *complementaryPin;           /**< \brief Complementary output pin configuration (_N) */
    Ifx_ActiveState     polarity;                   /**< \brief Active low/high of pin */
    Ifx_ActiveState     complementaryPolarity;      /**< \brief Active low/high of complementary pin */
    IfxPort_OutputMode  outputMode;                 /**< \brief Output mode */
    IfxPort_PadDriver   padDriver;                  /**< \brief Pad driver */
} IfxGtm_Pwm_OutputConfig;

/** \brief Channel run time structure
 */
typedef struct
{
    IfxGtm_Pwm_ChannelRegisters registers;         /**< \brief Contains pointers to frequenctly accessed channel specific registers */
    uint32                      upenMask;          /**< \brief Update enable mask of this channel */
    IfxGtm_Pwm_callBack         periodEvent;       /**< \brief CCU0 interrupt callback function pointer */
    IfxGtm_Pwm_callBack         dutyEvent;         /**< \brief CCU1 interrupt callback function pointer */
    IfxGtm_Pwm_SubModule_Ch     timerCh;           /**< \brief Channel Index */
    uint32                      phaseTicks;        /**< \brief Current phase ticks */
    uint32                      dutyTicks;         /**< \brief Current duty ticks */
} IfxGtm_Pwm_Channel;

/** \brief PWM Channel Configuration structure
 */
typedef struct
{
    IfxGtm_Pwm_SubModule_Ch     timerCh;         /**< \brief Channel Index */
    float32                     phase;           /**< \brief Initial phase in radians (range: 0.0 .. 2pi; only for edge aligned sync channels) */
    float32                     duty;            /**< \brief PWM duty in % (range: 0.0 .. 100.0) */
    IfxGtm_Pwm_DtmConfig       *dtm;             /**< \brief Dead time configuration for this channel */
    IfxGtm_Pwm_OutputConfig    *output;          /**< \brief Pin connections and polarities for this channel */
    IfxGtm_Trig_MscOut         *mscOut;          /**< \brief MSC Output configuration */
    IfxGtm_Pwm_InterruptConfig *interrupt;       /**< \brief Interrupt configuration for this channel */
} IfxGtm_Pwm_ChannelConfig;

/** \brief Clock source
 */
typedef union
{
    IfxGtm_Cmu_Clk   atom;       /**< \brief Clock source for ATOM channels */
    IfxGtm_Cmu_Fxclk tom;        /**< \brief Clock source for TOM channels */
} IfxGtm_Pwm_ClockSource;

typedef struct
{
    Ifx_GTM_ATOM *ATOM;       /**< \brief Pointer to cluster SFR */
    Ifx_GTM_TOM  *TOM;        /**< \brief Pointer to cluster SFR */
    Ifx_GTM_CDTM *CDTM;       /**< \brief Pointer to cluster SFR */
} IfxGtm_Pwm_ClusterSFR;

/** \brief Structure to hold pointers to [TGC[0/1]/AGC]_GLB_CTRL and mask value for fast access
 */
typedef struct
{
    volatile uint32 *reg0;                /**< \brief ATOM: points to AGC_GLB_CTRL. TOM: If channels span 2 TGCs then points to TGC0_GLB_CTRL else to the TGC being used TGCx_GLB_CTRL */
    volatile uint32 *reg1;                /**< \brief ATOM: Not used. TOM: Points to TGC1_GLB_CTRL if channels span across 2 TGCs. */
    uint32           upenMask0;           /**< \brief UPEN Mask for reg0 [AGC/TGC[0/1]]_GLB_CTRL */
    uint32           upenMask1;           /**< \brief UPEN Mask for reg1 [AGC/TGC[0/1]]_GLB_CTRL */
    volatile uint32 *endisCtrlReg0;       /**< \brief ATOM: points to AGC_ENDIS_CTRL.
                                           * TOM: If channels span 2 TGCs then points to TGC0_ENDIS_CTRL else to the TGC being used TGCx_GLB_CTRL */
    volatile uint32 *endisCtrlReg1;       /**< \brief ATOM: Not used. TOM: Points to TGC1_ENDIS_CTRL if channels span across 2 TGCs. */
} IfxGtm_Pwm_GlobalControl;

/** \brief PWM Handle
 */
typedef struct
{
    Ifx_GTM                 *gtmSFR;                  /**< \brief Pointer to GTM module */
    IfxGtm_Pwm_ClusterSFR    clusterSFR;              /**< \brief Cluster SFR */
    IfxGtm_Cluster           cluster;                 /**< \brief Index of the CLS object used */
    IfxGtm_Pwm_SubModule     subModule;               /**< \brief Sub module to be used for PWM */
    IfxGtm_Pwm_Alignment     alignment;               /**< \brief PWM alignment */
    uint8                    numChannels;             /**< \brief Number of channels configured (base + sync) */
    IfxGtm_Pwm_Channel      *channels;                /**< \brief Stores state of PWM channels (base + sync) */
    IfxGtm_Pwm_GlobalControl globalControl;           /**< \brief Pointer and mask for GLB_CTRL */
    float32                  sourceFrequency;         /**< \brief Source clock frequency in Hz */
    float32                  dtmFrequency;            /**< \brief DTM clock frequency in Hz */
    float32                  frequency;               /**< \brief Current PWM frequency in Hz */
    uint32                   periodTicks;             /**< \brief Current PWM Period in ticks */
    IfxGtm_Pwm_ClockSource   clockSource;             /**< \brief Clock source for Atom/Tom channels */
    IfxGtm_Dtm_ClockSource   dtmClockSource;          /**< \brief Clock source for DTM channels */
    boolean                  syncUpdateEnabled;       /**< \brief TRUE: Update compare registers from shadow at the end of period */
    IfxGtm_Pwm_State         state;                   /**< \brief Module state */
} IfxGtm_Pwm;

/** \brief PWM Configuration structure
 */
typedef struct
{
    Ifx_GTM                  *gtmSFR;                 /**< \brief Pointer to GTM module */
    IfxGtm_Cluster            cluster;                /**< \brief Index of the CLS object used */
    IfxGtm_Pwm_SubModule      subModule;              /**< \brief Sub module to be used for PWM */
    IfxGtm_Pwm_Alignment      alignment;              /**< \brief PWM alignment */
    uint8                     numChannels;            /**< \brief Number of channels (base + sync) to be configured */
    IfxGtm_Pwm_ChannelConfig *channels;               /**< \brief Pointer to channel configuration */
    float32                   frequency;              /**< \brief Initial PWM frequency */
    IfxGtm_Pwm_ClockSource    clockSource;            /**< \brief Clock source for Atom/Tom channels */
    IfxGtm_Dtm_ClockSource    dtmClockSource;         /**< \brief Clock source for DTM channels */
    boolean                   syncUpdateEnabled;      /**< \brief TRUE: Update compare registers from shadow at the end of period */
    boolean                   syncStart;              /**< \brief TRUE: Synchronously start all channels PWM by starting the counters at the end of init */
} IfxGtm_Pwm_Config;

/** \brief Configuration structure for output pin
 */
typedef struct
{
    IfxGtm_Pwm_ToutMap *outputPin;       /**< \brief Output pin */
    IfxPort_OutputMode  outputMode;      /**< \brief Output mode */
    IfxPort_PadDriver   padDriver;       /**< \brief Pad driver */
} IfxGtm_Pwm_Pin;

/******************************************************************************/
/*-------------------------Inline Function Prototypes-------------------------*/
/******************************************************************************/

/** \brief Sets the Signal level during duty cycle for specified channel
 * \param clusterSFR Pointer to the Cluster object
 * \param subModule ATOM/TOM
 * \param channel Channel index
 * \param polarity Active high/low
 * \return None
 */
IFX_INLINE void IfxGtm_Pwm_setChannelPolarity(IfxGtm_Pwm_ClusterSFR *clusterSFR, IfxGtm_Pwm_SubModule subModule, IfxGtm_Pwm_SubModule_Ch channel, Ifx_ActiveState polarity);

/** \brief Update sync channel phase with requested value at the end of period (synchronously)
 * Note: Use this API only for sync channels and not for base channel. API doesn't check this condition.
 * \param pwm PWM handle
 * \param configIndex Index of channel in the array of configured PWM channels
 * \param requestPhase Requested phase in radians (0.0 .. 2 * pi)
 * \return None
 */
IFX_INLINE void IfxGtm_Pwm_updateChannelPhase(IfxGtm_Pwm *pwm, IfxGtm_Pwm_SyncChannelIndex configIndex, float32 requestPhase);

/** \brief Update sync channel phase with requested value immediately (asynchronously)
 * Note: Use this API only for sync channels and not for base channel. API doesn't check this condition.
 * \param pwm PWM handle
 * \param configIndex Index of channel in the array of configured PWM channels
 * \param requestPhase Requested phase in radians (0.0 .. 2 * pi)
 * \return None
 */
IFX_INLINE void IfxGtm_Pwm_updateChannelPhaseImmediate(IfxGtm_Pwm *pwm, IfxGtm_Pwm_SyncChannelIndex configIndex, float32 requestPhase);

/** \brief Update channel duty with requested value at the end of period (synchronously)
 * \param pwm PWM handle
 * \param configIndex Index of channel in the array of configured PWM channels
 * \param requestDuty Requested duty in % (0.0 .. 100.0)
 * \return None
 */
IFX_INLINE void IfxGtm_Pwm_updateChannelDuty(IfxGtm_Pwm *pwm, IfxGtm_Pwm_SyncChannelIndex configIndex, float32 requestDuty);

/** \brief Update channel duty with requested value immediately (asynchronously)
 * \param pwm PWM handle
 * \param configIndex Index of channel in the array of configured PWM channels
 * \param requestDuty Requested duty in % (0.0 .. 100.0)
 * \return None
 */
IFX_INLINE void IfxGtm_Pwm_updateChannelDutyImmediate(IfxGtm_Pwm *pwm, IfxGtm_Pwm_SyncChannelIndex configIndex, float32 requestDuty);

/** \brief Update channel dead time (leading and trailing) with requested value (asynchronously)
 * \param pwm PWM handle
 * \param configIndex Index of channel in the array of configured PWM channels
 * \param requestDeadTime Requested dead time in seconds
 * \return None
 */
IFX_INLINE void IfxGtm_Pwm_updateChannelDeadTimeImmediate(IfxGtm_Pwm *pwm, IfxGtm_Pwm_SyncChannelIndex configIndex, IfxGtm_Pwm_DeadTime requestDeadTime);

/******************************************************************************/
/*-------------------------Global Function Prototypes-------------------------*/
/******************************************************************************/

/** \brief Initializes the configuration structure to default values
 * \param config Configuration structure
 * \param gtmSFR Pointer to GTM module
 * \return None
 */
IFX_EXTERN void IfxGtm_Pwm_initConfig(IfxGtm_Pwm_Config *config, Ifx_GTM *gtmSFR);

/** \brief Initializes the channel configuration structure to default values
 * \param channelConfig Configuration structure
 * \return None
 */
IFX_EXTERN void IfxGtm_Pwm_initChannelConfig(IfxGtm_Pwm_ChannelConfig *channelConfig);

/** \brief Initializes the GTM module for PWM
 * \param pwm PWM handle
 * \param channels Pointer to array to store channel data
 * Length of array is specified in config parameter
 * \param config Configuration structure
 * \return None
 */
IFX_EXTERN void IfxGtm_Pwm_init(IfxGtm_Pwm *pwm, IfxGtm_Pwm_Channel *channels, IfxGtm_Pwm_Config *config);

/** \brief Synchronously start all the configured channels
 * \param pwm PWM handle
 * \return None
 */
IFX_EXTERN void IfxGtm_Pwm_startSyncedChannels(IfxGtm_Pwm *pwm);

/** \brief Synchronously stops all the configured channels
 * \param pwm PWM handle
 * \return None
 */
IFX_EXTERN void IfxGtm_Pwm_stopSyncedChannels(IfxGtm_Pwm *pwm);

/** \brief Synchronously start two synced groups using TBU submodule of GTM
 * \param pwm1 First PWM handle
 * \param pwm2 Second PWM handle
 * \return None
 */
IFX_EXTERN void IfxGtm_Pwm_startSyncedGroups(IfxGtm_Pwm *pwm1, IfxGtm_Pwm *pwm2);

/** \brief Synchronously stops two synced groups using TBU submodule of GTM
 * \param pwm1 First PWM handle
 * \param pwm2 Second PWM handle
 * \return None
 */
IFX_EXTERN void IfxGtm_Pwm_stopSyncedGroups(IfxGtm_Pwm *pwm1, IfxGtm_Pwm *pwm2);

/** \brief Change the frequency of all channels to requested frequency at the end of period (synchronously)
 * \param pwm PWM handle
 * \param requestFrequency Requested frequency in Hz
 * \return None
 */
IFX_EXTERN void IfxGtm_Pwm_updateFrequency(IfxGtm_Pwm *pwm, float32 requestFrequency);

/** \brief Change the frequency of two synced groups' channels to requested frequency at the end of period (synchronously)
 * \param pwm1 PWM handle 1
 * \param pwm2 PWM handle 2
 * \param requestFrequency Requested frequency in Hz
 * \return None
 */
IFX_EXTERN void IfxGtm_Pwm_updateSyncedGroupsFrequency(IfxGtm_Pwm *pwm1, IfxGtm_Pwm *pwm2, float32 requestFrequency);

/** \brief Change the frequency of all channels to requested frequency immediately (asynchronously)
 * \param pwm PWM handle
 * \param requestFrequency Requested frequency in Hz
 * \return None
 */
IFX_EXTERN void IfxGtm_Pwm_updateFrequencyImmediate(IfxGtm_Pwm *pwm, float32 requestFrequency);

/** \brief Update phase and duty of any configured channel to requested values at the end of period (synchronously)
 * It is recommeded to use this API only for edge aligned PWM
 * \param pwm PWM handle
 * \param configIndex Enum to choose channel type (base/sync) and index
 * \param requestPhase New phase value (applicable for edge aligned sync channel)
 * \param requestDuty New duty value
 * \return None
 */
IFX_EXTERN void IfxGtm_Pwm_updateChannelPulse(IfxGtm_Pwm *pwm, IfxGtm_Pwm_SyncChannelIndex configIndex, float32 requestPhase, float32 requestDuty);

/** \brief Update phase and duty of any configured channel to requested values immediately (asynchronously)
 * It is recommeded to use this API only for edge aligned PWM
 * \param pwm PWM handle
 * \param configIndex Enum to choose channel type (base/sync) and index
 * \param requestPhase New phase value(applicable for edge aligned sync channel)
 * \param requestDuty New duty value
 * \return None
 */
IFX_EXTERN void IfxGtm_Pwm_updateChannelPulseImmediate(IfxGtm_Pwm *pwm, IfxGtm_Pwm_SyncChannelIndex configIndex, float32 requestPhase, float32 requestDuty);

/** \brief Update phase of all configured channels to requested values at the end of period (synchronously)
 * \param pwm PWM handle
 * \param requestPhase New phase values
 * Note: First index element is ignored
 * \return None
 */
IFX_EXTERN void IfxGtm_Pwm_updateChannelsPhase(IfxGtm_Pwm *pwm, float32 *requestPhase);

/** \brief Update duty of all configured channels to requested values at the end of period (synchronously)
 * \param pwm PWM handle
 * \param requestDuty New duty value
 * \return None
 */
IFX_EXTERN void IfxGtm_Pwm_updateChannelsDuty(IfxGtm_Pwm *pwm, float32 *requestDuty);

/** \brief Update phase and duty of all configured channels to requested values at the end of period (synchronously)
 * It is recommeded to use this API only for edge aligned PWM
 * \param pwm PWM handle
 * \param requestPhase New phase value(applicable for edge aligned sync channel)
 * \param requestDuty New duty value
 * \return None
 */
IFX_EXTERN void IfxGtm_Pwm_updateChannelsPulse(IfxGtm_Pwm *pwm, float32 *requestPhase, float32 *requestDuty);

/** \brief Update dead time of all configured channels to requested values
 * \param pwm PWM handle
 * \param requestDeadTime New dead time value
 * \return None
 */
IFX_EXTERN void IfxGtm_Pwm_updateChannelsDeadTimeImmediate(IfxGtm_Pwm *pwm, IfxGtm_Pwm_DeadTime *requestDeadTime);

/** \brief Update phase of all configured channels to requested values immediately (asynchronously)
 * \param pwm PWM handle
 * \param requestPhase New phase values
 * Note: First index element is ignored
 * \return None
 */
IFX_EXTERN void IfxGtm_Pwm_updateChannelsPhaseImmediate(IfxGtm_Pwm *pwm, float32 *requestPhase);

/** \brief Update duty of all configured channels to requested values immediately (asynchronously)
 * \param pwm PWM handle
 * \param requestDuty New duty value
 * \return None
 */
IFX_EXTERN void IfxGtm_Pwm_updateChannelsDutyImmediate(IfxGtm_Pwm *pwm, float32 *requestDuty);

/** \brief Update phase and duty of all configured channels to requested values immediately (asynchronously)
 * It is recommeded to use this API only for edge aligned PWM
 * \param pwm PWM handle
 * \param requestPhase New phase value(applicable for edge aligned sync channel)
 * \param requestDuty New duty value
 * \return None
 */
IFX_EXTERN void IfxGtm_Pwm_updateChannelsPulseImmediate(IfxGtm_Pwm *pwm, float32 *requestPhase, float32 *requestDuty);

/** \brief Function that handles interrupt generated by channel by calling ccu0/ccu1 callback functions
 * \param channel Channel handle
 * \param data Data to pass to callback functions
 * \return None
 */
IFX_EXTERN void IfxGtm_Pwm_interruptHandler(IfxGtm_Pwm_Channel *channel, void *data);

/** \brief Gets channel state: running or stopped
 * \param pwm PWM handle
 * \param channel Channel index
 * \return Channel state
 */
IFX_EXTERN IfxGtm_Pwm_ChannelState IfxGtm_Pwm_getChannelState(IfxGtm_Pwm *pwm, IfxGtm_Pwm_SubModule_Ch channel);

/******************************************************************************/
/*---------------------Inline Function Implementations------------------------*/
/******************************************************************************/

IFX_INLINE void IfxGtm_Pwm_setChannelPolarity(IfxGtm_Pwm_ClusterSFR *clusterSFR, IfxGtm_Pwm_SubModule subModule, IfxGtm_Pwm_SubModule_Ch channel, Ifx_ActiveState polarity)
{
    if (subModule == IfxGtm_Pwm_SubModule_atom)
    {
        IfxGtm_Atom_Ch_setSignalLevel(clusterSFR->ATOM, (IfxGtm_Atom_Ch)channel, polarity);
    }
    else                                                                  /* TOM */
    {
        IfxGtm_Tom_Ch_setSignalLevel(clusterSFR->TOM, (IfxGtm_Tom_Ch)channel, polarity);
    }
}


IFX_INLINE void IfxGtm_Pwm_updateChannelPhase(IfxGtm_Pwm *pwm, IfxGtm_Pwm_SyncChannelIndex configIndex, float32 requestPhase)
{
    IfxGtm_Pwm_Channel *channel = &pwm->channels[configIndex];

    /* Phase in ticks = period * (phase in radian / (2 * pi)) */
    uint32              phaseTicks = (uint32)((float32)(((float32)pwm->periodTicks * requestPhase * IFX_ONE_OVER_TWO_PI) + (float32)0.5f));

    /* Subtract old phase value and add new vale */
    uint32              dutyTicks = (channel->dutyTicks - channel->phaseTicks) + phaseTicks;

    /* Copy to handle */
    channel->phaseTicks = phaseTicks;
    channel->dutyTicks  = dutyTicks;

    /* Update shadow register */
    *channel->registers.SR0 = phaseTicks;
    *channel->registers.SR1 = (dutyTicks <= pwm->periodTicks) ? dutyTicks : dutyTicks - pwm->periodTicks;
}


IFX_INLINE void IfxGtm_Pwm_updateChannelPhaseImmediate(IfxGtm_Pwm *pwm, IfxGtm_Pwm_SyncChannelIndex configIndex, float32 requestPhase)
{
    IfxGtm_Pwm_Channel *channel = &pwm->channels[configIndex];

    /* Phase in ticks = period * (phase in radian / (2 * pi)) */
    uint32              phaseTicks = (uint32)((float32)(((float32)pwm->periodTicks * requestPhase * IFX_ONE_OVER_TWO_PI) + (float32)0.5f));

    /* Subtract old phase value and add new value */
    uint32              dutyTicks = (channel->dutyTicks - channel->phaseTicks) + phaseTicks;

    /* Copy to handle */
    channel->phaseTicks = phaseTicks;
    channel->dutyTicks  = dutyTicks;

    dutyTicks           = (dutyTicks <= pwm->periodTicks) ? dutyTicks : dutyTicks - pwm->periodTicks;

    /* Update compare register */
    *channel->registers.SR0 = phaseTicks;
    *channel->registers.SR1 = dutyTicks;
    *channel->registers.CM0 = phaseTicks;
    *channel->registers.CM1 = dutyTicks;
}


IFX_INLINE void IfxGtm_Pwm_updateChannelDuty(IfxGtm_Pwm *pwm, IfxGtm_Pwm_SyncChannelIndex configIndex, float32 requestDuty)
{
    IfxGtm_Pwm_Channel *channel = &pwm->channels[configIndex];

    /* Duty in ticks = period * (duty in percent / 100%) */
    uint32              dutyTicks = (uint32)((float32)(((float32)pwm->periodTicks * requestDuty * 0.01f) + (float32)0.5f));

    /* Subtract from period for center aligned */
    if (pwm->alignment != IfxGtm_Pwm_Alignment_edge)
    {
        dutyTicks          = pwm->periodTicks - dutyTicks;
        channel->dutyTicks = dutyTicks;
    }
    /* Offset by phase to account for phase shift in sync channel edge aligned (phaseticks = 0 for base channel) */
    else
    {
        dutyTicks         += channel->phaseTicks;
        channel->dutyTicks = dutyTicks;
        dutyTicks          = (dutyTicks <= pwm->periodTicks) ? dutyTicks : dutyTicks - pwm->periodTicks;
    }

    /* Update shadow register */
    *channel->registers.SR1 = dutyTicks;
}


IFX_INLINE void IfxGtm_Pwm_updateChannelDutyImmediate(IfxGtm_Pwm *pwm, IfxGtm_Pwm_SyncChannelIndex configIndex, float32 requestDuty)
{
    IfxGtm_Pwm_Channel *channel = &pwm->channels[configIndex];

    /* Duty in ticks = period * (duty in percent / 100%) */
    uint32              dutyTicks = (uint32)((float32)(((float32)pwm->periodTicks * requestDuty * 0.01f) + (float32)0.5f));

    /* Subtract from period for center aligned */
    if (pwm->alignment != IfxGtm_Pwm_Alignment_edge)
    {
        dutyTicks          = pwm->periodTicks - dutyTicks;
        channel->dutyTicks = dutyTicks;
    }
    /* Offset by phase to account for phase shift in sync channel edge aligned (phaseticks = 0 for base channel) */
    else
    {
        dutyTicks         += channel->phaseTicks;
        channel->dutyTicks = dutyTicks;
        dutyTicks          = (dutyTicks <= pwm->periodTicks) ? dutyTicks : dutyTicks - pwm->periodTicks;
    }

    /* Update compare register */
    *channel->registers.SR1 = dutyTicks;
    *channel->registers.CM1 = dutyTicks;
}


IFX_INLINE void IfxGtm_Pwm_updateChannelDeadTimeImmediate(IfxGtm_Pwm *pwm, IfxGtm_Pwm_SyncChannelIndex configIndex, IfxGtm_Pwm_DeadTime requestDeadTime)
{
    IfxGtm_Pwm_Channel *channel = &pwm->channels[configIndex];

    /* Only update if channel is connected to DTM */
    if (channel->registers.DTV != NULL_PTR)
    {
        Ifx_GTM_CDTM_DTM_CH_DTV dtvReg;

        /* Calculate values */
        uint32                  risingTicks  = (uint32)((float32)((requestDeadTime.rising * pwm->dtmFrequency) + (float32)0.5f));
        uint32                  fallingTicks = (uint32)((float32)((requestDeadTime.falling * pwm->dtmFrequency) + (float32)0.5f));

        /* Save dead time ticks */
        dtvReg.B.RELRISE = (uint16)risingTicks;
        dtvReg.B.RELFALL = (uint16)fallingTicks;

        /* Configure registers */
        *channel->registers.DTV = dtvReg.U;
    }
}


#endif /* IFXGTM_PWM_H */
